package cn.lei.netty.herostory.cmdhandler;

import cn.lei.netty.herostory.util.PackageUtil;
import com.google.protobuf.GeneratedMessageV3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 命令处理器工厂类
 *
 * @author lil
 */
public final class CmdHandlerFactory {

    /**
     * 日志对象
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(CmdHandlerFactory.class);

    /**
     * 命令处理器字典
     */
    private static final Map<Class<?>, ICmdHandler<? extends GeneratedMessageV3>> HANDLER_MAP = new ConcurrentHashMap<>();

    /**
     * 私有化默认构造器
     */
    private CmdHandlerFactory() {

    }

    /**
     * 工厂初始化
     */
    public static void init() {

        LOGGER.info("==== 完成命令与处理器关联 ====");
        //获取包名称
        final String packageName = CmdHandlerFactory.class.getPackage().getName();


        //获取 ICmdHandler 所有的实现类
        Set<Class<?>> clazzSet = PackageUtil.listSubClazz(packageName, true, ICmdHandler.class);

        for (Class<?> handlerClazz : clazzSet) {
            //不为空且不是抽象类
            if (handlerClazz == null || (handlerClazz.getModifiers() & Modifier.ABSTRACT) != 0) {
                continue;
            }

            //获取方法数组
            Method[] methodArray = handlerClazz.getDeclaredMethods();

            //消息类型
            Class<?> cmdClazz = null;
            for (Method currMethod : methodArray) {
                //过滤非空和非handle方法
                if (currMethod == null || !currMethod.getName().equals("handle")) {
                    continue;
                }

                //获取方法参数类型数组
                Class<?>[] paraTypeArray = currMethod.getParameterTypes();

                //参数列表参数为v3或者不是他的超类则不处理(如果不处理,那么会获取到接口的方法)
                if (paraTypeArray.length < 2 ||
                        paraTypeArray[1] == GeneratedMessageV3.class ||
                        !GeneratedMessageV3.class.isAssignableFrom(paraTypeArray[1])) {
                    continue;
                }

                //获取第二个参数类型
                cmdClazz = paraTypeArray[1];
                break;
            }

            if (cmdClazz == null) {
                continue;
            }

            try {
                //创建命令处理器实例
                ICmdHandler<?> newHandler = (ICmdHandler<?>) handlerClazz.newInstance();

                //记录类与方法参数的映射关系
                LOGGER.info("{} <==> {}", cmdClazz.getName(), handlerClazz.getName());

                //添加到命令处理器字典
                HANDLER_MAP.put(cmdClazz, newHandler);

            } catch (Exception ex) {
                //记录错误日志
                LOGGER.error(ex.getMessage(), ex);
            }

        }
    }

    /**
     * 创建命令处理器
     *
     * @param msgClazz 用户请求指令类型
     * @return
     */
    public static ICmdHandler<? extends GeneratedMessageV3> create(Class<?> msgClazz) {
        if (msgClazz == null) {
            return null;
        }
        return HANDLER_MAP.get(msgClazz);
    }
}
